<?php


/**
 * Overrides DirectoryIterator behavior by making it so that
 * only xml files are found
 */
class XmlDirectoryIterator extends DirectoryIterator {
	
	public function __construct( $dir ) {
		parent::__construct( $dir );
		$this->next();
	}
	
	public function next() {
		while( $this->valid() ) {
		   parent::next();
		   
		   if( $this->isDot() ) {
		   		continue;
		   }
		   if($this->isXmlFile()) {
		       return;
		   }
		}
	}
	
	public function isXmlFile() {
		$fileName = $this->getFilename();
		//echo "=== $fileName===\n";
		return strtolower(substr( $fileName, -4 )) == ".xml"  ? true : false;
	}	
}

class UIComponentDescriptor {
	public $class;
	public $baseClass;
	public $componentType;
	public $componentFamily;
	public $rendererType;
	public $shouldSaveState;
	public $shouldGenerateConstructor;
	public $fields;	
}

function indent($n) {
	return str_repeat( "\t", $n );	
}

$dirName = $argv[1];
$targetDir = $argv[2];

$componentDescriptors = array();

$dir = new XmlDirectoryIterator( $dirName );
while(  $dir->valid()  ) {
   
   //log: echo "Found XML File: ".$dir->getPathname()."\n";
   
   
   $doc = new DomDocument();
   $doc->load( $dir->getPathname() );
   
   $xpath = new DomXPath( $doc );
   $contextNode = $doc->documentElement;
   
   $uicd = new UIComponentDescriptor();
   
   $uicd->class = translateClass( $xpath->query( "component-class ", $contextNode )->item(0)->nodeValue );
   $uicd->baseClass = translateClass( $xpath->query( "base-class", $contextNode )->item(0)->nodeValue );
   $uicd->componentType = $xpath->query( "component-type", $contextNode )->item(0)->nodeValue;
   $uicd->componentFamily = $xpath->query( "component-family", $contextNode )->item(0)->nodeValue;
   $uicd->rendererType = $xpath->query( "renderer-type", $contextNode )->item(0)->nodeValue;
   $uicd->shouldSaveState = $contextNode->getAttribute( "generateStateMethods" ) == "true" ? true : false;
   $uicd->shouldGenerateConstructor = $contextNode->getAttribute( "generateConstructor" ) != "false" ? true : false;
   
   $uicd->fields = array();
   
   $fieldNodes = $xpath->query( "field", $contextNode );
   foreach( $fieldNodes as $fieldNode ) {
		$fieldName = $xpath->query( "name", $fieldNode )->item(0)->nodeValue;
		$fieldType = $xpath->query( "type", $fieldNode )->item(0)->nodeValue;
		
		$uicd->fields[] = array( $fieldName, $fieldType );
   }

	//echoComponentSource( $uicd );


	//echo echoComponentRow( $uicd );
	
	$componentDescriptors[ $uicd->class ] = $uicd;
   
   $dir->next();
}

$generatedClasses = array();
$outputBuffer = "";
/*
$classFD = fopen( "test.php", "rw" );
$currentClass = null;
$inGeneratedCodeSection = false;


while( $line = fgets( $classFD ) ) {
	if( preg_match("/[\s]*class[\s]*([\w\d_]+)/", $line, $m) ) {
		$className = $m[1];
		
		$currentClass = $className;
		//echo "found class ".$currentClass."\n";
		
		
		
		if( $componentDescriptor != null ) {
			
		}
	} 
	
	if( $currentClass && $line == "// ---- Begin Generated Code For $currentClass - DO NOT MODIFY ----\n" ) {
		$inGeneratedCodeSection = true;
		
		//echo "started generated code for $currentClass\n";
		
		//$outputBuffer .= $line;	
	} else if( $currentClass && $line == "// ---- End Generated Code For $currentClass - DO NOT MODIFY ----\n" ) {
		$uicd = $componentDescriptors[ $currentClass ];
		$outputBuffer .= getComponentSource( $uicd, false );
		$inGeneratedCodeSection = false;
		
		//echo "ended generated code for $currentClass\n";
		
		$generatedClasses[ $currentClass ] = 1;
		
		//$outputBuffer .= $line;	
	} else if( !$inGeneratedCodeSection ) {
		$outputBuffer .= $line;	
	}
}
fclose( $classFD );
*/
foreach( array_keys($componentDescriptors) as $className ) {
	if( !isset($generatedClasses[$className]) )	 {
		$uicd = $componentDescriptors[ $className ]; 
		$outputBuffer .= getComponentSource( $uicd, true );
	}
}

echo $outputBuffer;
//print_r( $generatedClasses );


function echoComponentRow( UIComponentDescriptor $uicd ) {
	echo <<<TR
<tr>
	<td>$uicd->class</td>
	<td>$uicd->baseClass</td>
	<td>$uicd->componentType</td>
	<td>$uicd->componentFamily</td>
	<td>$uicd->rendererType</td>
</tr>
TR;
}


function getComponentSource( UIComponentDescriptor $uicd, $generateClassDef = true ) {

	$phpSource = "";

	// ==================================================
	//			Class Header
	// ==================================================

	if( $generateClassDef ) {
		if( !$uicd->shouldGenerateConstructor ) {
			$phpSource .= "abstract ";
		}
		$phpSource .= "class $uicd->class";
		if( $uicd->baseClass ) { 
			$phpSource .= " extends $uicd->baseClass";
		}
		if( $uicd->shouldSaveState ) {
			$phpSource .= " implements PhpFaces_StateHolder";	
		}
		$phpSource .= " {\n";
	}
	
	$phpSource .= "// ---- Begin Generated Code For $uicd->class - DO NOT MODIFY ----\n";
	
	
	// ==================================================
	//			Constants
	// ==================================================
	
	
	if( $uicd->componentType ) {
		$phpSource .= indent(1).'const COMPONENT_TYPE = "'.$uicd->componentType.'";'."\n";
	}
	if( $uicd->componentFamily ) {
		$phpSource .= indent(1).'const COMPONENT_FAMILY = "'.$uicd->componentFamily.'";'."\n";
	}
	if( $uicd->rendererType ) {
		$phpSource .= indent(1).'const RENDERER_TYPE = "'.$uicd->rendererType.'";'."\n";
	}
	
	// ==================================================
	//			Constructor
	// ==================================================
	
	if( $uicd->shouldGenerateConstructor ) {
		$phpSource .= indent(1).'public function __construct() {'."\n";
		
		if( $uicd->rendererType ) {
			$phpSource .= indent(2).'$this->setRendererType( self::RENDERER_TYPE );'."\n";
		}
		
		$phpSource .= indent(1).'}'."\n";
	}
	
	if( $uicd->componentFamily ) {
		$phpSource .= indent(1).'public function getFamily() {'."\n";
		$phpSource .= indent(2).'return self::COMPONENT_FAMILY );'."\n";
		$phpSource .= indent(1).'}'."\n";
	}
	
	// ==================================================
	//			Fields
	// ==================================================
	
	foreach( $uicd->fields as $field ) {
		$fieldName = $field[0];
		$fieldType = $field[1];
		
		$phpSource .= indent(1)." /** @type $fieldType */\n";
		$phpSource .= indent(1)."private \$$fieldName;\n";
	}
	
	// ==================================================
	//			Field Accessors
	// ==================================================
	
	foreach( $uicd->fields as $field ) {
		$fieldName = $field[0];
		$fieldType = $field[1];

		$accessorName = ucfirst( $fieldName );
		$accessorPrefix = ( $fieldType == "boolean") ? "is" : "get";
		
		$phpSource .= indent(1)." /** @type $fieldType */\n";
		$phpSource .= indent(1)."public function $accessorPrefix$accessorName() {\n";
		$phpSource .= indent(2)."return \$this->$fieldName;\n";
		$phpSource .= indent(1)."}\n";
		
		$phpSource .= indent(1)." /** @return $fieldType */\n";
		$phpSource .= indent(1)."public function set$accessorName( \$newValue ) {\n";
		$phpSource .= indent(2)."\$this->$fieldName = \$newValue;\n";
		$phpSource .= indent(1)."}\n";
	}
	
	// ==================================================
	//			StateHolder
	// ==================================================
	
	if( $uicd->shouldSaveState ) {
		$phpSource .= indent(1)."function saveState( PhpFaces_Context_FacesContext \$context ) {\n";	
		$phpSource .= indent(2)."\$state = array();\n";
		
		$phpSource .= indent(2)."\$state[0] = parent::saveState( \$context );\n";
		
		foreach( $uicd->fields as $idx => $field ) {
			$fieldName = $field[0];
			$fieldType = $field[1];
			
			// for parent state
			$stateIdx = $idx + 1;
			
			$phpSource .= indent(2)."\$state[$stateIdx] = \$this->$fieldName;\n";
		}
		$phpSource .= indent(2)."return \$state;\n";
		$phpSource .= indent(1)."}\n";
		
		$phpSource .= indent(1)."function restoreState( PhpFaces_Context_FacesContext \$context, \$state ) {\n";	
		
		$phpSource .= indent(2)."parent::restoreState( \$context, \$state[0] );\n";
		
		foreach( $uicd->fields as $idx => $field ) {
			$fieldName = $field[0];
			$fieldType = $field[1];
			
			// for parent state
			$stateIdx = $idx + 1;
			
			$phpSource .= indent(2)."\$this->$fieldName = \$state[$stateIdx];\n";
		}
		$phpSource .= indent(1)."}\n";
	}
	
	// ==================================================
	//			End Of Class
	// ==================================================
	
	$phpSource .= "// ---- End Generated Code For $uicd->class - DO NOT MODIFY ----\n";
	
	
	if( $generateClassDef ) {
		$phpSource .= "}\n";
	}

	return $phpSource;	
}


function translateClass( $className ) {
		$className = str_replace( "javax.faces", "PhpFaces", $className );
		$className = str_replace( ".", "_", $className );
		
		$className = str_replace( "component_", "Component_", $className );
		$className = str_replace( "html_", "Html_", $className );
		
		return $className;
}


/*
<component generateStateMethods="true">
    <component-class>javax.faces.component.UICommand</component-class>
    <base-class>javax.faces.component.UIComponentBase</base-class>
    <component-type>javax.faces.Command</component-type>
    <component-family>javax.faces.Command</component-family>
    <renderer-type>javax.faces.Button</renderer-type>
    <field generateProperty="false">
        <name>action</name>
        <type>javax.faces.el.MethodBinding</type>
    </field>
    <field generateProperty="false">
        <name>actionListener</name>
        <type>javax.faces.el.MethodBinding</type>
    </field>
    <field>
        <name>immediate</name>
        <type>boolean</type>
        <default-value>false</default-value>
    </field>
    <field>
        <name>value</name>
        <type>java.lang.Object</type>
    </field>
</component>
 */
?>
